package com.zingrow.web.report.service.impl;

import java.awt.Dimension;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang3.StringUtils;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.itextpdf.text.BaseColor;
import com.itextpdf.text.Chunk;
import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Element;
import com.itextpdf.text.Font;
import com.itextpdf.text.Image;
import com.itextpdf.text.PageSize;
import com.itextpdf.text.Paragraph;
import com.itextpdf.text.Rectangle;
import com.itextpdf.text.pdf.BaseFont;
import com.itextpdf.text.pdf.PdfPCell;
import com.itextpdf.text.pdf.PdfPTable;
import com.itextpdf.text.pdf.PdfPageEventHelper;
import com.itextpdf.text.pdf.PdfWriter;
import com.zingrow.web.report.base.TableField;
import com.zingrow.web.report.response.ModuleData;
import com.zingrow.web.report.service.IDocExportor;
import com.zingrow.web.report.utils.DateUtils;
import com.zingrow.web.report.utils.ReportUtils;
import com.zingrow.web.report.utils.RptJsonUtils;

public class PdfDocExportor extends PdfPageEventHelper implements IDocExportor {

    private Document doc = new Document(PageSize.A4.rotate());

    public Document getDoc() {
        return doc;
    }

    public PdfDocExportor() {
        try {
            fonts = getFont(DocFont.values());
        } catch (DocumentException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public IDocExportor createDoc(Map<String, String> infos, List<ModuleData> moduleDatas, Map<String, String> svg,
            OutputStream out) {
        try {
            PdfWriter writer = PdfWriter.getInstance(getDoc(), out);
            writer.setStrictImageSequence(true);
            doc.open();
            writeLine();
            // 写入报告的标题信息
            writeText(infos.get(GAROUP_NAME), DocFont.TitleDoc, Align.Center);
            writeText(infos.get(RPT_NAME), DocFont.TitleDoc, Align.Center);
            writeLine();
            // 遍历获取到的模块数据信息，依次写入Word
            for (ModuleData moduleData : moduleDatas) {
                // 判断模块是否显示
                if (!moduleData.getShow()) {
                    continue;
                }
                // 写入模块标题
                String moduleTitle = moduleData.getTitle();
                writeLine();
                writeText(moduleTitle, DocFont.TitleModule, Align.Left);
                writeLine();
                // 写入模块的文字信息
                String moduleText = moduleData.getText();
                if (StringUtils.isNotBlank(moduleText)) {
                    writeText(moduleText, DocFont.Content, Align.Left);
                    writeLine();
                }
                // 开始插入图片
                String svgStr = svg.get(moduleData.getId() + "");
                // 根据展示类型判断是否加载并写入图片>2表示表格以上
                if (moduleData.getTypeId() > 2 && StringUtils.isNotBlank(svgStr)) {
                    byte[] bytes = ReportUtils.convertToPngByte(svgStr);

                    writeImage(bytes, null, null);
                } else if (moduleData.getTypeId() == 2 && !moduleData.getDataList().isEmpty()
                        && !moduleData.getTableFields().isEmpty()) {// 绘制表格
                    // 将数据信息转换为json数据
                    String dataJson = RptJsonUtils.toString(moduleData.getDataList(), "yyyy-MM-dd HH:mm:ss");
                    // 计算行数
                    int rowSize = 0;
                    if (moduleData.isPullByList()) {
                        rowSize = moduleData.getDataList().size() * moduleData.getDataList().get(0).getDatas().size();
                    } else {
                        rowSize = moduleData.getDataList().get(0).getDatas().size();
                    }
                    writeTable(moduleData.getTableFields(), dataJson, moduleData.isPullByList(), rowSize);
                } else if (moduleData.getTypeId() == 1 && moduleData.getDataList() != null
                        && moduleData.getDataList().size() > 0) {// 填充文字列表
                    String dataJson = RptJsonUtils.toString(moduleData.getDataList(), "yyyy-MM-dd HH:mm:ss");
                    writeTextList(moduleData.getTableFields(), dataJson);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return this;
    }

    @Override
    public void writeToStream() {
        doc.close();
    }

    @Override
    public void writeTable(List<TableField> fields, String dataJson, boolean isPullByList, int rowSize)
            throws Exception {
        // 获取列数量
        int colSize = fields.size();
        // 根据计算的行列来新建一个保存表格元素的二维数组（这里的行数不包含标题项）
        PdfPCell[][] cells = new PdfPCell[rowSize][colSize];
        // 从json数据中解析出树
        JsonNode root = new ObjectMapper().readTree(dataJson);
        // 拿到文档的总宽度
        Rectangle rectangle = getDoc().getPageSize();
        float width = rectangle.getWidth() - 80;
        // 这里计算每一列的宽度占比
        float[] widths = new float[colSize];
        for (int i = 0; i < colSize; i++) {
            widths[i] = width * fields.get(i).getWidthPercent();
        }
        // 开始生成表格
        PdfPTable table = new PdfPTable(colSize);
        // 设置宽度
        table.setTotalWidth(widths);
        table.setLockedWidth(true);
        BaseColor lightGrey = new BaseColor(0xCC, 0xCC, 0xCC);
        BaseColor firstColor = new BaseColor(77, 83, 97);
        // 下面的for生成标题栏
        for (TableField field : fields) {
            // 设置标题信息
            Font font = new Font(fonts.get(DocFont.TitleModule));
            font.setColor(BaseColor.WHITE);
            Paragraph titlePhrase = new Paragraph(field.getTitle(), font);
            PdfPCell titleCell = new PdfPCell(titlePhrase);
            // 设置标题高度和样式
            titleCell.setFixedHeight(TABLE_HEIGHT);
            titleCell.setHorizontalAlignment(Element.ALIGN_CENTER);
            titleCell.setVerticalAlignment(Element.ALIGN_MIDDLE);
            titleCell.setBorderColor(lightGrey);
            titleCell.setBackgroundColor(firstColor);
            // 添加标题到表格中
            table.addCell(titleCell);
        }
        // 行数计数器.为了精准添加数据到表格的坐标而计算的行数值
        int row = 0;
        // 开始解析并填充表格数据
        Iterator<JsonNode> moduleDataIterator = root.elements();
        while (moduleDataIterator.hasNext()) {
            // 如果不是按照普通的表格排列，则表格的行数不是数据类型列表数*值列表数，而只是数据列表数
            // 故重新设置行数为0
            if (!isPullByList) {
                row = 0;
            }
            // 拿出第一层数据列表
            JsonNode typeData = moduleDataIterator.next();
            // 获取这个数据的标题
            String dataName = typeData.findValue("title").asText();
            // 获取到这一行的数据
            JsonNode dataList = typeData.path("datas");
            // 在类型数据中获取真正值数据的迭代。
            Iterator<JsonNode> dataIterator = dataList.elements();
            while (dataIterator.hasNext()) {
                // 这个data代表一个数据信息，即最终取到的包含值的对象
                JsonNode data = dataIterator.next();
                // 再次遍历列标题信息数组，进行field匹配，匹配到的则取出值
                for (int i = 0; i < colSize; i++) {
                    // 获取这一列定义的的标题信息
                    TableField field = fields.get(i);
                    PdfPCell cell = null;
                    // 拼接显示的文本，定义一个文本对象
                    StringBuilder value = new StringBuilder();
                    String dataListField = field.getDataListField();
                    String valueField = field.getValueField();
                    // 格式化文本的format
                    String textFormat = field.getTextFormat();
                    // 格式化日期的format
                    String dateFormat = field.getTimeFormat();
                    // 超链接的field
                    String urlField = field.getUrlField();

                    // 若匹配到此列跟此行数据来源一致
                    if (StringUtils.isNotBlank(dataName) && StringUtils.isNotBlank(dataListField)
                            && dataListField.equals(dataName)) {
                        String str = "";
                        // 首先判断是否需要自增的序号
                        if (field.isIncr()) {
                            value.append((row + 1) + "");
                        }
                        // 取出文本值
                        if (StringUtils.isNotBlank(valueField)) {
                            try {
                                str = data.findValue(valueField).asText();
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                            if (StringUtils.isNotBlank(textFormat)) {
                                // 判断是否需要格式化填充文本
                                str = String.format(textFormat, str);
                            } else if (StringUtils.isNotBlank(dateFormat)) {
                                // 判断是否需要格式化文本日期
                                try {
                                    str = DateUtils.printDate(DateUtils.paraseStringToDate(str, "yyyy-MM-dd HH:mm:ss"),
                                            field.getTimeFormat());
                                } catch (Exception e) {
                                }
                            }
                            // 最后拼接文本则为最后显示的
                            value.append(str);
                        }
                        // 这里字体需要新建对象，否则影响其他的字体
                        Font font = fonts.get(DocFont.Content);
                        if (i == 0) {
                            // 为第一列时，由于背景为黑色，字体需要为白色
                            font = new Font(fonts.get(DocFont.Content));
                            font.setColor(BaseColor.WHITE);
                        }
                        // 创建字体和表格元素对象
                        Chunk chunk = new Chunk(value.toString(), font);
                        // 添加超链接
                        if (StringUtils.isNotBlank(urlField)) {
                            chunk.setAnchor(data.findValue(urlField).asText());
                        }
                        Paragraph cellPhrase = new Paragraph();
                        cellPhrase.add(chunk);
                        cell = new PdfPCell(cellPhrase);
                        cell.setFixedHeight(TABLE_HEIGHT);
                        // 设置此条数据是否需要居中显示
                        cell.setHorizontalAlignment(field.isCenter() ? Element.ALIGN_CENTER : Element.ALIGN_LEFT);
                        cell.setVerticalAlignment(Element.ALIGN_MIDDLE);
                        cell.setBorderColor(lightGrey);
                        // cell.setPaddingTop(-2f);//把字垂直居中
                        // cell.setPaddingBottom(8f);//把字垂直居中
                        if (i == 0) {
                            cell.setBackgroundColor(firstColor);
                        }
                        // 将表格元素加入到对应位置的二维数组中
                        cells[row][i] = cell;
                    } else {
                        // 没有找到匹配的field字段
                        continue;
                    }
                }
                // 每循环一个数据对象，行数+1
                row++;
            }
        }

        // 添加数据进表格中表格
        for (PdfPCell[] cellsCols : cells) {
            for (PdfPCell cellsRow : cellsCols) {
                table.addCell(cellsRow == null ? new PdfPCell() : cellsRow);
            }
        }
        // 表格添加到文档
        getDoc().add(table);
    }

    @Override
    public void writeImage(byte[] bytes, Dimension size, Align align) throws Exception {
        if (size == null) {
            size = ReportUtils.getImgDimension(new ByteArrayInputStream(bytes));
        }
        if (size == null) {
            return;
        }
        if (align == null) {
            align = Align.Center;
        }
        Image image = Image.getInstance(bytes);
        image.setAlignment(getAlignment(align));
        image.scalePercent(getPercent2(image.getHeight(), image.getWidth()) + 3);
        getDoc().add(image);
    }

    @Override
    public void writeText(String text, DocFont font, Align align) throws Exception {
        Paragraph paragraph = new Paragraph(text, fonts.get(font));
        paragraph.setAlignment(getAlignment(align));
        getDoc().add(paragraph);
    }

    @Override
    public void writeTextList(List<TableField> fields, String dataJson) throws Exception {
        // 获取列数量
        int colSize = fields.size();
        // 从json数据中解析出树
        JsonNode root = new ObjectMapper().readTree(dataJson);
        int row = 0;
        // 开始解析并填充表格数据
        Iterator<JsonNode> moduleDataIterator = root.elements();
        while (moduleDataIterator.hasNext()) {
            // 拿出第一层数据列表
            JsonNode typeData = moduleDataIterator.next();
            // 获取这个数据的标题
            String dataName = typeData.findValue("title").asText();
            // 获取到这一行的数据
            JsonNode dataList = typeData.path("datas");
            // 在类型数据中获取真正值数据的迭代。
            Iterator<JsonNode> dataIterator = dataList.elements();
            while (dataIterator.hasNext()) {
                // 这个data代表一个数据信息，即最终取到的包含值的对象
                JsonNode data = dataIterator.next();
                // 再次遍历列标题信息数组，进行field匹配，匹配到的则取出值
                for (int i = 0; i < colSize; i++) {
                    // 获取这一列定义的的标题信息
                    TableField field = fields.get(i);
                    // 拼接显示的文本，定义一个文本对象
                    StringBuilder value = new StringBuilder();
                    String dataListField = field.getDataListField();
                    String valueField = field.getValueField();
                    // 格式化文本的format
                    String textFormat = field.getTextFormat();
                    // 格式化日期的format
                    String dateFormat = field.getTimeFormat();

                    // 若匹配到此列跟此行数据来源一致
                    if (StringUtils.isNotBlank(dataName) && StringUtils.isNotBlank(dataListField)
                            && dataListField.equals(dataName)) {
                        String str = "";
                        // 首先判断是否需要自增的序号
                        if (field.isIncr()) {
                            value.append((row + 1) + "");
                        }
                        // 取出文本值
                        if (StringUtils.isNotBlank(valueField)) {
                            str = data.findValue(field.getValueField()).asText();
                            if (StringUtils.isNotBlank(textFormat)) {
                                // 判断是否需要格式化填充文本
                                str = String.format(textFormat, str);
                            } else if (StringUtils.isNotBlank(dateFormat)) {
                                // 判断是否需要格式化文本日期
                                try {
                                    str = DateUtils.printDate(DateUtils.paraseStringToDate(str, "yyyy-MM-dd HH:mm:ss"),
                                            field.getTimeFormat());
                                } catch (Exception e) {
                                }
                            }
                            // 最后拼接文本则为最后显示的
                            value.append(str);
                        }
                        // 创建字体和表格元素对象
                        // 这里字体需要新建对象，否则影响其他的字体
                        Font font = fonts.get(DocFont.Content);
                        if (i == 0) {
                            // 为第一列时，由于背景为黑色，字体需要为白色
                            font = new Font(fonts.get(DocFont.Content));
                            font.setStyle(Font.BOLD);
                        }
                        Paragraph paragraph = new Paragraph(value.toString(), font);
                        paragraph.setFirstLineIndent(20);
                        getDoc().add(paragraph);

                    } else {
                        // 没有找到匹配的field字段
                        continue;
                    }
                }
                // 每循环一个数据对象，行数+1
                writeLine();
                row++;
            }
        }
    }

    @Override
    public void writeLine() throws Exception {
        Paragraph br = new Paragraph("\n", fonts.get(DocFont.Content));
        getDoc().add(br);
    }

    private int getAlignment(Align align) {
        int alignment = 1;
        switch (align) {
        case Center:
            alignment = Element.ALIGN_CENTER;
            break;
        case Left:
            alignment = Element.ALIGN_LEFT;
            break;
        case Right:
            alignment = Element.ALIGN_RIGHT;
            break;
        default:
            alignment = Element.ALIGN_CENTER;
            break;
        }
        return alignment;
    }

    private Map<DocFont, Font> fonts = null;

    private Map<DocFont, Font> getFont(DocFont... font) throws DocumentException, IOException {
        Map<DocFont, Font> fonts = new HashMap<>();
        BaseFont bfChinese = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
        for (DocFont docFont : font) {
            fonts.put(docFont, new Font(bfChinese, docFont.getSize(), docFont.getBold() ? Font.BOLD : Font.NORMAL));
        }
        return fonts;
    }

    public int getPercent2(float h, float w) {
        int p = 0;
        float p2 = 0.0f;
        p2 = 530 / w * 100;
        p = Math.round(p2);
        return p;
    }
}
